package com.hup.joystick.service;

import com.hup.joystick.model.DeviceConfig;
import com.hup.joystick.model.joystick.AutoConnectConfig;
import com.hup.joystick.service.jna.DIJoyState2;
import com.hup.joystick.service.jna.DInputHResultException;
import com.hup.joystick.service.jna.DInputHResultException.HResult;
import com.hup.joystick.service.jna.DeviceStateCallback;
import com.hup.joystick.service.jna.EnumDeviceData;
import com.hup.joystick.service.joystick.AutoConnectListener;
import com.hup.joystick.service.joystick.JoyEventListener;
import com.hup.joystick.service.joystick.JoystickService;
import com.hup.utils.commons.exception.AppCommonException;
import com.hup.utils.commons.thread.ThreadUtil;
import com.sun.jna.NativeLong;
import com.sun.jna.platform.KeyboardUtils;
import lombok.extern.log4j.Log4j2;

import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 用于一般UI开发测试,不连接dll,返回模拟数据
 *
 * @author hugan
 * @date 2020/10/20
 */
@Log4j2
public class JoystickServiceMocker extends JoystickService implements DeviceStateCallback {

    private DIJoyState2 lastUpdateState = DIJoyState2.EMPTY_STATE;

    @Override
    public void initDirectInput() throws DInputHResultException {
        log.info("initDirectInput.st");
        DInputHResultException.testThrow("初始化DirectInput", HResult.S_OK.val);
    }

    @Override
    public ArrayList<EnumDeviceData> enumDevices() throws DInputHResultException {
        notInUIThread();
        ThreadLocalRandom random = ThreadLocalRandom.current();
        if (KeyboardUtils.isPressed(KeyEvent.VK_CONTROL))  DInputHResultException.testThrow("获取设备列表", HResult.DIERR_UNSUPPORTED.val);
        ArrayList<EnumDeviceData> res = new ArrayList<>();
        if (random.nextInt(0, 10) < 3) res.add(new EnumDeviceData("设备1-模拟", "aax"));
        if (random.nextInt(0, 10) < 8) res.add(new EnumDeviceData("HID 设备1-模拟", "0x26b96020"));
        return res;
    }

    @Override
    public void startListenState(String guid, int stateScanSpan, int stickRange, JoyEventListener joyEventListener) {
        log.info("stateScanSpan={}", stateScanSpan);
        stopListenState(true);
        log.info("启动监听线程");
        stateExecutor.execute(() -> {
            //模拟调用JNA的阻塞型api
            log.info("listen.st");
            listeningState = true;
            JoystickServiceMocker.this.joyEventListener = joyEventListener;
            while (listeningState) {
                long second = System.currentTimeMillis() / 1000;
                DIJoyState2 state = DIJoyState2.emptyState();
                if (second % 3 == 0) {
                    state.lX = new NativeLong(30);
                }
                if (!state.equals(lastUpdateState)) {
                    onDeviceState(state, HResult.S_OK.val);
                    lastUpdateState = state;
                }
                ThreadUtil.sleep(stateScanSpan);
            }
            //线程结束前,发送一次LOST消息
            onDeviceState(DIJoyState2.EMPTY_STATE, HResult.DIERR_INPUT_LOST.val);

            JoystickServiceMocker.this.joyEventListener = null;
            listeningState = false;
            log.info("listen.ed");
        });
    }

    @Override
    public void startAutoConnect(AutoConnectConfig connectConfig, AutoConnectListener connectListener) {
        stopListenState(true);
        stopAutoConnect(true);
        autoConnectExecutor.execute(() -> {
            log.info("autoConnect.st");
            autoConnecting = true;
            try {
                //获取手柄配置
                List<DeviceConfig> lstConfig = connectListener.getConfig();
                if (KeyboardUtils.isPressed(KeyEvent.VK_CONTROL)) lstConfig.clear();//test
                if (lstConfig.isEmpty()) throw new AppCommonException("无可用的手柄配置");

                while (autoConnecting) {
                    connectListener.onScanning();
                    ArrayList<EnumDeviceData> lstOnline = enumDevices();
                    log.info("online={}", lstOnline.size());
                    //lstOnline.clear();//test

                    //查找设备
                    DeviceConfig toConnect = connectListener.findDevice(lstOnline, lstConfig);
                    lstOnline.clear();
                    if (toConnect == null) {
                        sleepScanDevice(connectConfig.deviceScanSpan);
                        continue;
                    }

                    //开始监听
                    try {
                        listeningState = true;
                        JoystickServiceMocker.this.joyEventListener = connectListener;
                        connectListener.beforeConnect(toConnect);
                        while (listeningState) {
                            if (KeyboardUtils.isPressed(KeyEvent.VK_ALT)) {
                                log.info("模拟设备断开");
                                break;
                            }

                            DIJoyState2 state = DIJoyState2.emptyState();
                            long second = System.currentTimeMillis() / 1000;
                            if (second % 3 == 0) state.lX = new NativeLong(30);
                            if (!state.equals(lastUpdateState)) {
                                onDeviceState(state, HResult.S_OK.val);
                                lastUpdateState = state;
                                if (KeyboardUtils.isPressed(KeyEvent.VK_SHIFT))
                                    throw new IllegalArgumentException("模拟异常");
                            }
                            ThreadUtil.sleep(connectConfig.stateScanSpan);
                        }
                        log.info("INPUT_LOST");
                        //结束前,发送一次LOST消息
                        onDeviceState(DIJoyState2.EMPTY_STATE, HResult.DIERR_INPUT_LOST.val);
                    } finally {
                        listeningState = false;
                        JoystickServiceMocker.this.joyEventListener = null;
                    }
                    //达到此处:设备断开
                }
                //达到此处:调用了stop方法
                autoConnecting = false;
            } catch (Throwable e) {
                autoConnecting = false;
                connectListener.onException(e);
            }
            log.info("autoConnect.ed");
        });
    }

    @Override
    protected void doFreeDirectInput() {
        log.info("");
    }

}
